#include "Entity.h"

Entity::Entity() {
    VarInit(vec3(0.0f, 1.0f, 0.0f));
}

Entity::Entity(vec3 initial) {
    VarInit(initial);
}

void Entity::SetModelData(const std::string& filename, GLuint newTexture, RenderManager rManager) {
    mask.SetModelData(rManager);
    
    ModelRender *newModel = new ModelRender(true);
    
    newModel->SetManager(rManager);
    newModel->Initialize(filename);
    newModel->SetTexture(newTexture);
    
    models.push_back(newModel);
    
    model = models[0];
}

void Entity::GameLoop(float dt) {
    if (inputState.space) {
        Jump();
    }

    ApplyPhysics(.0f);
    TestCollisions();
    UpdatePosition();
    UpdateState();
}

void Entity::Jump() {
    if (mIsOnGround) {
        mVelocity.y = 3.0f;
        mIsOnGround = false;
    }
}

void Entity::AddToCollisions(BoundingBox add) {
   collisions.push_back(add);
}

void Entity::Render(bool hitbox, float dt) {
   if (hitbox) {
      mask.Render();
   }
   else {
      model->StackStack();
         model->Scale(vec3(0.25f));
         model->Rotate(direction, vec3(0.f, 1.f, 0.f));
         model->Translate(vec3(mPosition.x, mPosition.y + 0.4f, mPosition.z));
         model->Draw(dt);
      model->EndStack();
   }
}

void Entity::ApplyPhysics(float dt) {
   vec3 friction = vec3(0.0f);
   vec3 gravity = vec3(0.0f, -3.0f, 0.0f);
   vec3 input = vec3(0.0f);
    
   if (inputState.left) {
      input.x -= 5.0f;
   }

   if (inputState.right) {
      input.x += 5.0f;
   }

   if (mVelocity.x > 0.05f) {
      direction = RIGHT;
      friction.x = -3.0f;
   }
   else if (mVelocity.x < -0.05f) {
      direction = LEFT;
      friction.x = 3.0f;
   }

   // SET accel to 0
   mAccel = vec3(0.0f, 0.0f, 0.0f);

   // SET mNext to the current position
   mNextPos = vec3(mPosition);

   // If there was no time-step previously, timestep an average
   if (dt == 0) {
      dt = 0.016f;
   }

   // SET the acceleration to the gravity
   mAccel = gravity + friction + input;

   // pos = pos0 + vel0*dt + 0.5*a*dt*dt
   mNextPos = mPosition + (mVelocity * dt) + (mAccel * (dt * dt * 0.5f));

   // vel = vel + a*dt
   mVelocity = mVelocity + (mAccel * dt);

   // Update the velocity vector
   RoundVector();
}

void Entity::RoundVector() {
    // IF the rounded X velocity and acceleration is 0, and the unrounded one isn't
    if (fabs(mVelocity.x) < 0.05f && (mAccel.x < 0.001f && mAccel.x > -0.001f)) {
        // SET the X velocity to 0
        mVelocity.x = 0.0f;
    }
    
    if (fabs(mVelocity.y) < 0.05f && (mAccel.y < 0.001f && mAccel.y > -0.001f)) {
        // SET the X velocity to 0
        mVelocity.y = 0.0f;
    }
}

void Entity::TestCollisions() {
    mask.SetCorner(vec3(mNextPos.x, mPosition.y, mPosition.z) - vec3(0.25f, 0.0f, -0.25f));

    for (int i = 0; i < collisions.size(); i++) {
        if (collisions[i].CollidesWith(mask)) {
            mNextPos.x = mPosition.x;
            mVelocity.x = 0.0f;
        }
    }
    
    mask.SetCorner(vec3(mPosition.x, mNextPos.y, mPosition.z) - vec3(0.25f, 0.0f, -0.25f));

    for (int i = 0; i < collisions.size(); i++) {
        if (collisions[i].CollidesWith(mask)) {
            mNextPos.y = mPosition.y;
            mVelocity.y = 0.0f;
            mIsOnGround = true;
        }
    }
    
}

void Entity::UpdateState() {
   if (!mIsOnGround) {
      if (models.size() > 2 && model != models[2]) {
         // Jump Animation
         model = models[2];
         model->ResetAnimationTime();
      }
      
   }
   else if (mVelocity.x > 0.03f || mVelocity.x < -0.03f) {
      if (models.size() > 2 && model != models[1]) {
         // Running Animation
         model = models[1];
         model->ResetAnimationTime();
      }
   }
   else {
      if (models.size() > 2 && model != models[0]) {
         // Idle Animation
         model = models[0];
         model->ResetAnimationTime();
      }
   }
}

void Entity::UpdatePosition() {
   if (mPosition.y - mNextPos.y > 0.05f) {
      mIsOnGround = false;
   }
   
   mPosition.x = mNextPos.x;
   mPosition.y = mNextPos.y;
   mPosition.z = mNextPos.z; 
   
   if (mPosition.y < -3.0f) {
      printf("DEAD\n");
      mIsOnGround = false;
      mPosition.y = 2.0f;
      mPosition.x = 0.0f;
      mVelocity = vec3(0.0f);
   }
}

void Entity::UpdateState(std::string key, bool value) {
    if (key == "left") {
        inputState.left = value;
    }
    else if (key == "right") {
        inputState.right = value;
    }
    else if (key == "space") {
        inputState.space = value;
    }
}
